package com.leyou.httpdemo.utils;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.PDFTextStripperByArea;
import org.apache.pdfbox.text.TextPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * pdf工具类
 *
 * @author raos
 * @date 2024-03-26 20:39
 */
public class PdfUtil {

    private static final Logger LOG = LoggerFactory.getLogger(PdfUtil.class);

    public static final String DEFAULT_STR = "";

    /***
     * 获取流中pdf文件内容（不支持扫码件类型的pdf）
     * @param is 文件流
     * @return pdf文件内容
     */
    public static String readPdfTxt(InputStream is) {
        String text = DEFAULT_STR;
        try (PDDocument document = PDDocument.load(is)) {
            // 创建一个PDF文本提取器
            PDFTextStripper pdfStripper = new PDFTextStripper();
            // 提取文本内容
            text = pdfStripper.getText(document);
            LOG.info("pdf文档内容提取完毕...");
        } catch (Exception e) {
            LOG.error("解析流中pdf文档内容失败", e);
        }
        return text;
    }

    /***
     * 获取pdf文件内容（不支持扫码件类型的pdf）
     * @param file 文件
     * @return pdf文件内容
     */
    public static String readPdfTxt(File file) {
        String text = DEFAULT_STR;
        try {
            text = readPdfTxt(Files.newInputStream(file.toPath()));
        } catch (Exception e) {
            LOG.error("解析文件中pdf文档内容失败", e);
        }
        return text;
    }

    /***
     * 依据流获取清理后的pdf文件内容
     */
    public static String getCleanReadPdfTxt(InputStream is) {
        return cleanText(readPdfTxt(is));
    }

    /***
     * 依据文件获取清理后的pdf文件内容
     */
    public static String getCleanReadPdfTxt(File file) {
        return cleanText(readPdfTxt(file));
    }

    /***
     * 清理文本中的多余不符字符
     * @param text 文本字符串
     * @return 清理后的字符串
     */
    public static String cleanText(String text) {
        if (!StringUtils.hasLength(text)) {
            LOG.info("输入字符为Null或空串，返回默认串");
            return DEFAULT_STR;
        }
        // 去除多余空格符
        String cleanedText = text.replaceAll("[\\r\\t]+", " ") // 替换回车符和制表符为空格
                .replaceAll(" +", " ") // 替换多个连续空格为一个空格
                .trim(); // 去除字符串首尾的空格
        // 清理中文间的空格
        cleanedText = cleanedText.replaceAll("([\\u4e00-\\u9fa5])[ ]+(?=[\\u4e00-\\u9fa5])", "$1");
        // 清理可能留下的多余空格
        cleanedText = cleanedText.replaceAll(" +", " ").trim();
        return cleanedText;
    }

    /***
     * 查询关键字在pdf文件中的位置
     * @param is pdf文件内容流
     * @param keyword 关键字
     * @return 位置列表
     */
    public static List<PdfPosition> searchKeywordInPDF(InputStream is, String keyword) {
        List<PdfPosition> keywordPositions = new ArrayList<>();
        try (PDDocument document = PDDocument.load(is)) {
            Assert.isTrue(StringUtils.hasLength(keyword), "关键字不能为空！");
            PDFTextStripper stripper = new PDFTextStripper() {
                @Override
                protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
                    // 这里调用默认的writeString方法来保持PDFTextStripper的功能
                    super.writeString(string, textPositions);

                    // 检查每个文本片段是否包含关键字
                    int index;
                    while ((index = string.indexOf(keyword)) != -1) {
                        // 获取关键字在文本片段中的位置
                        TextPosition startPosition = textPositions.get(index);
                        TextPosition endPosition = textPositions.get(index + keyword.length() - 1);

                        // 计算关键字的首尾位置（可能需要进一步调整以获取准确的坐标）
                        float xStart = startPosition.getXDirAdj();
                        float yStart = startPosition.getYDirAdj();
                        float xEnd = endPosition.getXDirAdj();
                        float yEnd = endPosition.getYDirAdj();

                        // 这里可以记录关键字的位置信息，注意：这里的坐标可能需要进一步处理才能得到准确的页面上的位置
                        PdfPosition pos = new PdfPosition(getCurrentPageNo(), xStart, yStart, xEnd, yEnd);
                        keywordPositions.add(pos);

                        // 移动索引以继续搜索字符串的其余部分
                        string = string.substring(index + keyword.length());
                    }
                }
            };
            // 设置按页提取文本
            stripper.setSortByPosition(true);
            stripper.setStartPage(0);
            stripper.setEndPage(document.getNumberOfPages());

            // 提取文本并触发writeString方法
            stripper.getText(document);
        } catch (Exception e) {
            LOG.error("查询关键位置失败", e);
        }
        return keywordPositions;
    }

    /***
     * 查询关键字在pdf文件中的位置
     * @param file 文件
     * @param keyword 关键字
     * @return 位置列表
     */
    public static List<PdfPosition> searchKeywordInPDF(File file, String keyword) {
        List<PdfPosition> positions = new ArrayList<>();
        try {
            positions = searchKeywordInPDF(Files.newInputStream(file.toPath()), keyword);
        } catch (IOException e) {
            LOG.error("查询关键字在pdf文档内容位置失败", e);
        }
        return positions;
    }



    private static class SearchTask implements Callable<List<PdfPosition>> {
        private final int page; // pdf读取时页码数下标从0开始计算
        private final PDDocument document;
        private final String keyword;

        public SearchTask(int page, PDDocument document, String keyword) {
            this.page = page;
            this.document = document;
            this.keyword = keyword;
        }

        @Override
        public List<PdfPosition> call() throws Exception {
            List<PdfPosition> positions = new ArrayList<>();
            PDFTextStripperByArea stripper = new PDFTextStripperByArea() {
                @Override
                protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
                    // 这里调用默认的writeString方法来保持PDFTextStripper的功能
                    super.writeString(string, textPositions);

                    // 检查每个文本片段是否包含关键字
                    int index;
                    while ((index = string.indexOf(keyword)) != -1) {
                        // 获取关键字在文本片段中的位置
                        TextPosition startPosition = textPositions.get(index);
                        TextPosition endPosition = textPositions.get(index + keyword.length() - 1);

                        // 计算关键字的首尾位置（可能需要进一步调整以获取准确的坐标）
                        float xStart = startPosition.getXDirAdj();
                        float yStart = startPosition.getYDirAdj();
                        float xEnd = endPosition.getXDirAdj();
                        float yEnd = endPosition.getYDirAdj();

                        // 这里可以记录关键字的位置信息，注意：这里的坐标可能需要进一步处理才能得到准确的页面上的位置
                        PdfPosition pos = new PdfPosition((page + 1), xStart, yStart, xEnd, yEnd);
                        positions.add(pos);

                        // 移动索引以继续搜索字符串的其余部分
                        string = string.substring(index + keyword.length());
                    }
                }
            };
            // 创建一个矩形区域来匹配整个页面（这里假设页面没有旋转）
            PDRectangle rect = document.getPage(page).getMediaBox();
            stripper.addRegion("page" + page, rect.toGeneralPath().getBounds2D());
            stripper.extractRegions(document.getPage(page));
            return positions;
        }
    }

    public static List<PdfPosition> searchKeyword(ExecutorService executor, File file, String keyword) {
        List<PdfPosition> allPositions = new ArrayList<>();
        try (PDDocument document = PDDocument.load(file)) {
            int numPages = document.getNumberOfPages();
            // 提交所有页面的搜索任务
            List<Future<List<PdfPosition>>> futures = new ArrayList<>();
            for (int i = 0; i < numPages; i++) {
                Future<List<PdfPosition>> future = executor.submit(new SearchTask(i, document, keyword));
                futures.add(future);
            }

            // 收集所有页面的搜索结果
            for (Future<List<PdfPosition>> future : futures) {
                if (!CollectionUtils.isEmpty(future.get())) {
                    allPositions.addAll(future.get());
                }
            }
        } catch (Exception e) {
            LOG.error("多线程关键字匹配pdf内容失败", e);
        }
        return allPositions;
    }


    public static void main(String[] args) {
        // 创建一个固定大小的线程池
//        LOG.info("当前主机适合的线程数：{}", Runtime.getRuntime().availableProcessors());
        ExecutorService executor = Executors.newFixedThreadPool(16);
        long start = System.currentTimeMillis();
        try {
            // PDF文件路径
            String fileName = "E:\\raos\\工作目录\\2024年\\202403\\0315-0331\\危险货物品名表_256页(OCR).pdf";
            // 内容匹配速度多线程相对单线程快20%（整体而已）
            for (int i = 0; i < 10; i++) {
//                List<PdfPosition> list = searchKeywordInPDF(new File(fileName), "危险货物品");
//                LOG.info("查询结果数目：{}", list.size());
//                list.forEach(System.out::println);
                List<PdfPosition> list2 = searchKeyword(executor, new File(fileName), "危险货物品");
                LOG.info("查询结果数目：{}", list2.size());
                list2.forEach(System.out::println);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("耗时毫秒数：" + (System.currentTimeMillis() - start));
            executor.shutdown();
        }
    }

}
